#include "dht.h"
#include "Delay.h"

// GPIO初始化为输出
void DHT11_Init_Out(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP; // 推挽输出
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOx, &GPIO_InitStructure);
}

// GPIO初始化为输入（浮空）
void DHT11_Init_In(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING; // 浮空输入
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOx, &GPIO_InitStructure);
}

// 主机发送开始信号时序
void DHT11_Start(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
    DHT11_Init_Out(GPIOx, GPIO_Pin); // 输出模式
    GPIO_SetBits(GPIOx, GPIO_Pin); // 先拉高
    Delay_us(30);
    GPIO_ResetBits(GPIOx, GPIO_Pin); // 拉低电平至少18ms
    Delay_ms(20);
    GPIO_SetBits(GPIOx, GPIO_Pin); // 拉高电平20~40us
    Delay_us(30);
    DHT11_Init_In(GPIOx, GPIO_Pin); // 输入模式，设置为浮空输入
}

// 获取一个字节
char DHT11_Rec_Byte(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
    unsigned char i = 0;
    unsigned char data = 0;

    for (i = 0; i < 8; i++) // 1个数据就是1个字节byte，1个字节byte有8位bit
    {
        while (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == 0); // 从1bit开始，低电平变高电平，等待低电平结束
        Delay_us(35); // 延迟35us是为了区别数据0和数据1，0只有26~28us

        data <<= 1; // 左移

        if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == 1) // 如果过了35us还是高电平的话就是数据1
        {
            data |= 1; // 数据+1
        }

        while (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == 1); // 高电平变低电平，等待高电平结束
    }
    return data;
}

// 获取数据
void DHT11_REC_Data(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, unsigned int data[])
{
    unsigned int R_H, R_L, T_H, T_L;
    unsigned char RH, RL, TH, TL, CHECK;

    DHT11_Start(GPIOx, GPIO_Pin); // 主机发送信号
    GPIO_SetBits(GPIOx, GPIO_Pin); // 拉高电平

    if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == 0) // 判断DHT11是否响应, DHT11收到信号后会拉低总线
    {
        while (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == 0); // 低电平变高电平，等待低电平结束
        while (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == 1); // 高电平变低电平，等待高电平结束

        R_H = DHT11_Rec_Byte(GPIOx, GPIO_Pin);
        R_L = DHT11_Rec_Byte(GPIOx, GPIO_Pin);
        T_H = DHT11_Rec_Byte(GPIOx, GPIO_Pin);
        T_L = DHT11_Rec_Byte(GPIOx, GPIO_Pin);
        CHECK = DHT11_Rec_Byte(GPIOx, GPIO_Pin); // 接收5个数据

        GPIO_ResetBits(GPIOx, GPIO_Pin); // 当最后一bit数据传送完毕后，DHT11拉低总线 50us
        Delay_us(55); // 这里延时55us
        GPIO_SetBits(GPIOx, GPIO_Pin); // 随后总线由上拉电阻拉高进入空闲状态。

        if (R_H + R_L + T_H + T_L == CHECK) // 和检验位对比，判断校验接收到的数据是否正确
        {
            RH = R_H;
            RL = R_L;
            TH = T_H;
            TL = T_L;
        }
    }
    data[0] = RH;
    data[1] = RL;
    data[2] = TH;
    data[3] = TL;
}
